#ifndef PUDGE_META_MONAD_HPP
#define PUDGE_META_MONAD_HPP

#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <vector>

namespace pudge { namespace meta {

	template<class Function, class Iterator> struct monadic {};

	/// don't use this class, I might roll this into a monad specialization...

	template<class R, class A0, class I> struct monadic<R (A0),I>
	{
		typedef monadic<R (A0),I> self_type;

		virtual ~monadic() {}
		virtual R call(A0,I,I) = 0;

		template<class Result> struct call_
		{
			template<class Derived> R operator()(Derived & d, A0 a0, I b, I e) const
			{
				Result result(d(a0));
				return (*++b)->call(result, b, e);
			}
		};
		template<> struct call_<R>
		{
			template<class Derived> R operator()(Derived & d, A0 a0, I b, I e) const
			{
				if(b + 1 == e)
					return d(a0);

				R result(d(a0));
				return (*++b)->call(result, b, e);
			}
		};
		template<class T> struct derived : self_type
		{
			virtual ~derived() {}
			virtual R call(A0 a0, I b, I e)
			{
				return call_<T::result<A0>::type>()
					(	*static_cast<T*>(this)
					,	a0
					,	b
					,	e );
			}
		};
		struct compose
		{
			I begin_;
			I end_;

			compose(I b, I e) : begin_(b), end_(e) {}

			R operator()(A0 a0)
			{
				if(begin_ == end_) throw -1;

				return (*begin_)->call(a0, begin_, end_);
			}
		};
	};

	/// A monad/composite. 
	///
	/// I'm not sure the term monad is justly applied to this type, but the
	/// word is short.
	///
	///	The last function called must return the exact Result type specified.  
	///
	/// monad functions must define
	/// template<class T0, ..., class TN> struct result { typedef derived_result type; };

#ifndef PUDGE_META_MONAD_PARAMS
#define PUDGE_META_MONAD_PARAMS 3
#endif

	template<class Function> struct monad {};

	template<class R, class T1>
	struct monad<R (T1)>
	{
		typedef monad<R (T1)> self_type;
		typedef R result_type;
		typedef T1 t1_type;

		struct base;

		typedef std::vector<base*> list_type;
		typedef typename list_type::iterator iterator;

		list_type list_;

		struct base
		{
			virtual ~base(){}
			virtual R call(T1,iterator,iterator) = 0;
		};
		template<class Function> struct function : base
		{
			Function function_;
			function(Function & function__) : function_(function__) {}

			template<class Derived> struct call_
			{
				R operator ()(Function & f, T1 t1, iterator b, iterator e) const
				{
					Derived result(f(t1));
					return (*++b)->call(result, b, e);
				}
			};
			template<> struct call_<R>
			{
				R operator ()(Function & f, T1 t1, iterator b, iterator e) const
				{
					if(b + 1 == e)
						return f(t1);

					R result(f(t1));
					return (*++b)->call(result, b, e);
				}
			};
			virtual ~function(){}
			virtual R call(T1 t1, iterator b, iterator e)
			{
				return call_<Function::result<T1>::type>()(function_, t1, b, e);
			}
		};
		virtual ~monad()
		{
			for(list_type::iterator i = list_.begin(); i != list_.end(); ++i)
				delete *i;
		}
		template<class Function> self_type & operator += (Function & function_)
		{
			list_.push_back(new function<Function>(function_));
			return *this;
		}
		void clear()
		{
			for(list_type::iterator i = list_.begin(); i != list_.end(); ++i)
				delete *i;
			list_.clear();
		}
		R operator ()(T1 t1)
		{
			list_type::iterator begin (list_.begin());
			list_type::iterator end (list_.end());

			if(begin == end) throw -1;

			return (*begin)->call(t1, begin, end);
		}
	};

#define BOOST_PP_FILENAME_1 "pudge/meta/detail/unroll_monad.hpp"
#define BOOST_PP_ITERATION_LIMITS (2,PUDGE_META_MONAD_PARAMS)
#include BOOST_PP_ITERATE()

}}

#endif